package com.aifast.common.fieldEncrypt;

import cn.hutool.core.util.ObjectUtil;
import org.apache.ibatis.binding.MapperMethod;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.util.*;

/**
 * @Author fujian
 * @since 2022/9/12 9:50
 */
@Component
@Intercepts({
        @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})
})
public class EncryptInterceptor implements Interceptor {

    private final List<String> mappedKeyPrefixes = Arrays.asList(new String[]{"et"});
    @Resource
    private AiEncryptor aiEncryptor;

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        Object[] args = invocation.getArgs();
        MappedStatement ms = (MappedStatement) args[0];
        SqlCommandType sqlCommandType = ms.getSqlCommandType();
        Object parameter = args[1];
//        Console.log("EncryptInterceptor拦截器开始工作......");
//        Console.log(sqlCommandType);
        if ((SqlCommandType.INSERT == sqlCommandType || SqlCommandType.UPDATE == sqlCommandType) && ObjectUtil.isNotNull(parameter)) {
            if (parameter instanceof MapperMethod.ParamMap) {
                MapperMethod.ParamMap<Object> paramMap = (MapperMethod.ParamMap<Object>) parameter;
                handleMap(paramMap);
            } else {
                handleEntity(parameter);
            }
        }
        return invocation.proceed();
    }

    private void handleMap(MapperMethod.ParamMap<Object> paramMap) {
        Set<Map.Entry<String, Object>> entries = paramMap.entrySet();
        for (Map.Entry<String, Object> entry : entries) {
            String key = entry.getKey();
            Object value = entry.getValue();
            if (ObjectUtil.isNull(key) || ObjectUtil.isNull(value)) {
                continue;
            }
            mappedKeyPrefixes.forEach(i -> {
                if (!key.startsWith(i)) {
                    return;
                }
                if (value instanceof ArrayList) {
                    ArrayList list = (ArrayList) value;
                    if (!list.isEmpty()) {
                        Object firstItem = list.get(0);
                        Class<?> itemClass = firstItem.getClass();
                        Set<Field> encryptedFields = FieldsProvider.get(itemClass);
                        for (Object item : list) {
                            set(encryptedFields, item);
                        }
                    }
                } else {
                    set(FieldsProvider.get(value.getClass()), value);
                }
            });
        }

    }

    private void handleEntity(Object parameter) {
        set(FieldsProvider.get(parameter.getClass()), parameter);

    }

    private void set(Set<Field> encryptedFields, Object item) {
        if (ObjectUtil.isNull(encryptedFields) || encryptedFields.size() == 0) {
            return;
        }
        encryptedFields.forEach(i -> {
            FieldEncrypt annotation = i.getAnnotation(FieldEncrypt.class);
            if (ObjectUtil.isNull(annotation)) {
                return;
            }
            try {
                i.setAccessible(true);
                Object rawData = i.get(item);
                if (ObjectUtil.isNull(rawData) || ObjectUtil.isEmpty(rawData)) {
                    return;
                }
                String encrypt = aiEncryptor.encrypt(rawData, annotation.type(), annotation.key());
                i.set(item, encrypt);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        });

    }

    @Override
    public Object plugin(Object target) {
        return Interceptor.super.plugin(target);
    }

    @Override
    public void setProperties(Properties properties) {
        Interceptor.super.setProperties(properties);
    }
}
